﻿using System;
using System.Threading;
using OMR.Crawler.Core.Collections;
using OMR.Crawler.Core.Extensions;

namespace OMR.Crawler.Core.Crawlers
{
    public abstract class CrawlerBase
    {
        private static object _lockObject = new object();

        #region Events

        public event EventHandler<CrawlStartedEventArgs> OnCrawlStarted;

        public event EventHandler<CrawlFinishedEventArgs> OnCrawlFinished;

        public event EventHandler<BeginItemCrawlingEventArgs> OnBeginItemCrawling;

        public event EventHandler<CrawledItemEventArgs> OnItemCrawled;

        public event EventHandler<CrawlErrorEventArgs> OnCawlError;

        #endregion

        protected readonly MyCollection Settings;
        protected MultiThreadQueue<CrawlWork> CrawlQueue;
        protected readonly int MaxThreadCount;
        protected int RunningThreadCount;

        protected CrawlerBase(MyCollection settings)
        {
            this.Settings = settings;
            this.MaxThreadCount = (int)settings.GetItem("MaxThreadCount").Value;
            this.RunningThreadCount = 0;
            this.CrawlQueue = new MultiThreadQueue<CrawlWork>(MaxThreadCount);
        }

        private void Process(Action<CrawlWork> action, CrawlWork currentWork)
        {
            try
            {
                action(currentWork);
            }
            catch (Exception exception)
            {
                CrawlError(exception, currentWork.EntryPoint);
            }
            finally
            {
                Interlocked.Decrement(ref RunningThreadCount);
            }
        }

        protected void Begin(Action<CrawlWork> action)
        {
            OnCrawlStarted.Raise(this);

            CrawlQueue.ProceedMultiThread((crawlWork) => Process(action, crawlWork));
            
            CrawlFinished();
        }

        protected void ItemCrawled(CrawlWork crawlWork)
        {
            OnItemCrawled.Raise(this, (x) => x.Current = crawlWork);
        }

        protected void CrawlError(Exception exception, string EntyPoint)
        {
            OnCawlError.Raise(this, (x) => x.Exception = exception);
        }

        protected void CrawlFinished()
        {
            End();
        }

        public abstract void Begin();

        public virtual void End()
        {
            OnCrawlFinished.Raise(this);
        }

    }
}
